home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Commun⁄Network / RevRdist Folder / RevRdist / RevRdist src / volops.c < prev   
Encoding:
C/C++ Source or Header  |  1992-08-17  |  16.6 KB  |  690 lines  |  [TEXT/KAHL]

  1. /*
  2.  * volops.c - routines for misc volume operations
  3.  */
  4. #include "RevRdist.h"
  5. #include <HyperXCmd.h>
  6. #include <stdarg.h>
  7. #include <string.h>
  8. #include <Errors.h>
  9. #include "volops.h"
  10.  
  11. /*
  12.  * list of volumes we have unmounted and so might need to remount
  13.  */
  14. struct    mchain
  15. {
  16.     Handle            next;
  17.     AFPVolMountInfo    info;
  18. };
  19. typedef    struct    mchain    mchain;
  20. static    Handle        Remount_list;            /* first mchain entry */
  21. /*
  22.  * Volume we mounted and so might need to unmount
  23.  */
  24. static    Integer        Mounted_vref;
  25.  
  26. pascal void        callback(void);
  27. OSErr            callxcmd (Integer, Handle *rh, ...);
  28. OSErr            mountsvr (AFPVolMountInfo *, Boolean);
  29. Handle            ptoch (Str31);
  30. void            unmount_conflicts (Integer, AFPVolMountInfo *);
  31.  
  32.  
  33.  
  34. /*
  35.  *=========================================================================
  36.  * accessMaster(fn, mi) - gain access to master folder
  37.  * entry:    fn = name of master folder
  38.  *            mi = ptr to AppleShare info to mount folder
  39.  * exit:    returns 0 on success, *rh possibly updated
  40.  *            else OSErr
  41.  *=========================================================================
  42.  */
  43. OSErr
  44. accessMaster (StringPtr fn, AFPVolMountInfo *mi)
  45. {
  46.     OSErr            error;
  47.     CInfoPBPtr        cp;
  48.     int                pass;
  49.     char            *s;
  50.     StringPtr        sn;                    /* server name */
  51.     HParamBlockRec    pb;
  52.     Str31            volName;
  53.  
  54.     cp = (CInfoPBPtr) &pb;
  55.     for (pass = 0; pass < 3; pass++)
  56.     {
  57.         /*
  58.          * See if we have see files, see folders access to master folder
  59.          */
  60.         ZEROAT(cp);
  61.         cp->dirInfo.ioNamePtr = fn;
  62.         error = PBGetCatInfo (cp, false);
  63.         if (error == 0)
  64.             if ((cp->dirInfo.ioFlAttrib & ioDirMask) == 0)
  65.                 error = dupFNErr;
  66.         if (error == 0)
  67.             if ((pb.accessParam.ioACUser & 0x03) != 0)
  68.                 error = afpAccessDenied;
  69.         if (error == 0)
  70.             return 0;                /* got it */
  71.  
  72.         /*
  73.          * Try various things to get master folder mounted
  74.          */
  75.         switch (pass)
  76.         {
  77.         case 0:
  78.             /*
  79.              * First, simply try mounting the volume containing it
  80.              */
  81.             sn = (StringPtr)mi + mi->serverNameOffset;
  82.             if (sn[0] == 0)
  83.                 return error;        /* if no server, can do nothing */
  84.             error = mountsvr (mi, true);
  85.             break;
  86.         case 1:
  87.             /*
  88.              * If that didn't do it, we may need to unmount conflicting
  89.              * volumes and then try mounting again.
  90.              */
  91.             ZERO(pb);
  92.             s = (char *)mi + mi->volNameOffset;
  93.             COPYPS (s, volName);
  94.             if (volName[volName[0]] != ':')
  95.                 volName[++volName[0]] = ':';
  96.             pb.volumeParam.ioNamePtr = volName;
  97.             pb.volumeParam.ioVolIndex = -1;
  98.             error = PBGetVInfo ((ParmBlkPtr)&pb, false);
  99.             unmount_conflicts (error ? 0 : pb.volumeParam.ioVRefNum, mi);
  100.             error = mountsvr (mi, true);
  101.             if (error == 0)
  102.                 break;
  103.         }
  104.     }
  105.     return error ? error : dirNFErr;
  106. }
  107.  
  108.  
  109.  
  110. /*
  111.  *=========================================================================
  112.  * callback () - dummy XCMD callback routine
  113.  *    should never be called
  114.  *=========================================================================
  115.  */
  116. pascal
  117. void
  118. callback ()
  119. {
  120. }
  121.  
  122.  
  123.  
  124. /*
  125.  *=========================================================================
  126.  * callxcmd (xh, rh [,args]) - call XCMD with args
  127.  * entry:    resid = resource id of XCMD resource
  128.  *            rh = ptr to handle to contain XCMD result
  129.  *            args = Str255's to pass to XCMD
  130.  * returns:    0 if no errors, else OSErr
  131.  *=========================================================================
  132.  */
  133. OSErr
  134. callxcmd (Integer resid, Handle *rh, ...)
  135. {
  136.     va_list        ap;
  137.     OSErr        error;
  138.     int            i;
  139.     StringPtr    sp;
  140.     Handle        h;
  141.     struct XCmdBlock cb;            /* XCMD parameter block */
  142.     typedef    pascal void (*xcmd)(struct XCmdBlock *);
  143.  
  144.     /*
  145.      * Convert args from Strings to handles to C strings and stash in parameter
  146.      * block.
  147.      */
  148.     va_start (ap, rh);
  149.     error = memFullErr;
  150.     ZERO (cb);
  151.     for (i = 0; i < (sizeof(cb.params)/sizeof(cb.params[0]));)
  152.     {
  153.         sp = va_arg (ap, StringPtr);
  154.         if (sp == 0)
  155.             break;
  156.         cb.params[i++] = h = ptoch (sp);
  157.         if (h == 0)
  158.             goto cleanup;
  159.     }
  160.     /*
  161.      * Fill in the rest of the parameter block and call the XCMD
  162.      */
  163.     error = 0;
  164.     cb.paramCount = i;
  165.     cb.entryPoint = (ProcPtr) callback;
  166.     h = GetResource ('XCMD', resid);
  167.     if (!h)
  168.     {
  169.         error = ResError();
  170.         if (error == 0)
  171.             error = resNotFound;
  172.         goto cleanup;
  173.     }
  174.     HLock (h);
  175.     (*(xcmd)(*h)) (&cb);            /* call the XCMD */
  176.     HUnlock (h);
  177.     ReleaseResource(h);
  178.     /*
  179.      * dispose of our strings, etc.
  180.      */
  181. cleanup:
  182.     va_end (ap);
  183.     for (--i; i >= 0; --i)
  184.     {
  185.         if (h = cb.params[i])
  186.             DisposHandle (h);
  187.     }
  188.     *rh = cb.returnValue;                /* check the result */
  189.     return error;
  190. }
  191.  
  192.  
  193.  
  194. /*
  195.  *=========================================================================
  196.  * findWvol (vn, flags, volp) - find first volume matching criteria
  197.  * entry:    vn = str255 to hold vol name
  198.  *            flags = bits selecting criteria FF_xxx
  199.  *            volp = pointer to Integer to hold volume refnum
  200.  * returns:    0 if found, else OSErr
  201.  *=========================================================================
  202.  */
  203. OSErr
  204. findWvol (StringPtr vn, int flags, Integer *volp)
  205. {
  206.     OSErr                error;
  207.     int                    i;
  208.     HParamBlockRec        hpb;            /* HFS parameter block */
  209.     GetVolParmsInfoBuffer vib;            /* for GetVolParms */
  210.  
  211.     ZERO (hpb);
  212.     hpb.volumeParam.ioNamePtr = vn;
  213.     for (i = 1; ; i++)
  214.     {
  215.         vn[0] = 0;
  216.         hpb.volumeParam.ioVRefNum = 0;
  217.         hpb.volumeParam.ioVolIndex = i;
  218.         error = PBHGetVInfo (&hpb, false);
  219.         if (error)
  220.             break;
  221.         if (flags & FF_WRITE && hpb.volumeParam.ioVAtrb & 0x8080)
  222.             continue;                /* skip if S/W or H/W locked */
  223.         if (flags & FF_NOSONY && hpb.volumeParam.ioVDRefNum == -5)
  224.             continue;                /* skip if Sony driver */
  225.         if (flags & FF_NOXFS && hpb.volumeParam.ioVFSID)
  226.             continue;                /* skip if foreign file system */
  227.         if (flags & ~(FF_WRITE|FF_NOSONY))
  228.         {
  229.             ZERO(vib);
  230.             hpb.ioParam.ioBuffer = (Ptr) &vib;
  231.             hpb.ioParam.ioReqCount = sizeof (vib);
  232.             error = PBHGetVolParms (&hpb, false);
  233.             if (error && error != paramErr)
  234.                 continue;
  235.             if (error == 0)
  236.             {
  237.                 if (flags & FF_NOXFS && vib.vMAttrib & (1<<bHasExtFSVol))
  238.                     continue;            /* another check for foreign fs */
  239.                 if (flags & FF_NOASHARE && vib.vMServerAdr)
  240.                     continue;
  241.             }
  242.             error = 0;
  243.         }
  244.         *volp = hpb.volumeParam.ioVRefNum;
  245.         break;
  246.     }
  247.     return error;
  248. }
  249.  
  250.  
  251. /*
  252.  *=========================================================================
  253.  * mountsvr (mi, flag) - try to mount volume from server
  254.  * entry:    mi = pointer to mounting information
  255.  *            flag = true if should record mounted volume
  256.  * returns:    0 if volume mounted
  257.  *            <> 0 on error
  258.  *=========================================================================
  259.  */
  260. static
  261. OSErr
  262. mountsvr (AFPVolMountInfo *mi, Boolean flag)
  263. {
  264.     OSErr            error;
  265.     int                i;                /* temp index */
  266.     Handle            rh;                /* XCMD response handle */
  267.     StringPtr        sv;                /* server volume name */
  268.     StringPtr        zn;                /* zone name */
  269.     StringPtr        sn;                /* server name */
  270.     StringPtr        un;                /* user name */
  271.     StringPtr        pw;                /* user password */
  272.     ParamBlockRec    pb;                /* simple parameter block */
  273.     HParamBlockRec    hpb;            /* HFS parameter block */
  274.     Str255            volName;        /* returned volume name */
  275.     AFPVolMountInfo    micopy;            /* copy of mi parameter */
  276.  
  277.     rh = 0;
  278.     sv = (StringPtr)mi + mi->volNameOffset;
  279.     /*
  280.      * PBVolumeMount does not reliably return afpAlreadyMounted,
  281.      * so check ourselves if already mounted.
  282.      */
  283.     ZERO (hpb);
  284.     hpb.volumeParam.ioNamePtr = volName;
  285.     for (i = 1, error = 0; ! error; i++)
  286.     {
  287.         volName[0] = 0;
  288.         hpb.volumeParam.ioVRefNum = 0;
  289.         hpb.volumeParam.ioVolIndex = i;
  290.         error = PBHGetVInfo (&hpb, false);
  291.         if (error == 0 && EqualString (sv, volName, false, true))
  292.         {
  293.             return 0;
  294.         }
  295.     }
  296.     /*
  297.      * PBVolumeMount alters the mounting information record, so we
  298.      * make a copy and let it alter the copy.
  299.      */
  300.     ZERO(pb);
  301.     micopy = *mi;
  302.     pb.ioParam.ioBuffer = (Ptr)&micopy;
  303.     error = PBVolumeMount (&pb);
  304.     if (error && error != paramErr && error != afpAlreadyMounted)
  305.         return error;
  306.     if (error == afpAlreadyMounted)
  307.         flag = false;            /* don't record mount by us */
  308.     sv = (StringPtr)mi + mi->volNameOffset;
  309.     if (error == paramErr)
  310.     {
  311.         /*
  312.          * An error of paramErr really means that the PBVolumeMount HFSDispatch
  313.          * subfunction is not supported.
  314.          * Use the Apple Mount XCMD to try to mount the volume from
  315.          * an AppleShare server.
  316.          */
  317.         zn = (StringPtr)mi + mi->zoneNameOffset;
  318.         sn = (StringPtr)mi + mi->serverNameOffset;
  319.         un = (StringPtr)mi + mi->userNameOffset;
  320.         pw = (StringPtr)mi + mi->userPasswordOffset;
  321.         error = callxcmd (RSRC_MOUNT, &rh, zn, sn, sv,
  322.                         un[0] ? un : NULL, pw[0] ? pw : NULL, NULL);
  323.         if (rh && *rh && **rh)
  324.             error = fnfErr;
  325.         if (error)
  326.             goto cleanup;
  327.     }
  328.     /*
  329.      * Now, scan to make sure the volume was really mounted
  330.      */
  331.     ZERO (hpb);
  332.     hpb.volumeParam.ioNamePtr = volName;
  333.     for (i = 1, error = 0; ! error; i++)
  334.     {
  335.         volName[0] = 0;
  336.         hpb.volumeParam.ioVRefNum = 0;
  337.         hpb.volumeParam.ioVolIndex = i;
  338.         error = PBHGetVInfo (&hpb, false);
  339.         if (EqualString (sv, volName, false, true))
  340.         {
  341.             /*
  342.              * Found it, everything is cool.
  343.              */
  344.             if (flag)
  345.                 Mounted_vref = hpb.volumeParam.ioVRefNum;
  346.             break;
  347.         }
  348.     }
  349. cleanup:
  350.     if (rh)
  351.         DisposHandle (rh);
  352.     return error;
  353. }
  354.  
  355.  
  356.  
  357. /*
  358.  *=========================================================================
  359.  * ptoch (ps) - copy pascal string to c string in new handle
  360.  * entry:    ps = ptr to pascal string
  361.  * returns:    handle to null-terminated copy of string
  362.  *            0 if cannot allocate handle
  363.  *=========================================================================
  364.  */
  365. static
  366. Handle
  367. ptoch (ps)
  368. register unsigned char *ps;
  369. {
  370. register Handle h;
  371. register int    i;
  372. register unsigned char *s;
  373.  
  374.     i = ps[0];
  375.     h = NewHandle ((Size) (i + 1));
  376.     if (h)
  377.     {
  378.         for (s = (unsigned char *)(*h); i; i--)
  379.             *s++ = *++ps;
  380.         *s = 0;
  381.     }
  382.     return h;
  383. }
  384.  
  385.  
  386.  
  387.  
  388. /*
  389.  *=========================================================================
  390.  * remount_vols () - remount volumes unmounted by unmount_conflicts
  391.  * entry:    Remount_list points to chain of mount information
  392.  * returns:    OSErr
  393.  *=========================================================================
  394.  */
  395. OSErr
  396. remount_vols (void)
  397. {
  398.     OSErr            error;
  399.     Handle            h, next, prev;        /* links */
  400.     mchain            *mp;                /* current mount chain entry */
  401.     OSErr            result;
  402.  
  403.     /*
  404.      * The remount chain is current in backward order, reverse it
  405.      */
  406.     result = 0;
  407.     for (prev = 0, h = Remount_list; h ; h = next)
  408.     {
  409.         mp = (mchain *)(*h);
  410.         next = mp->next;
  411.         mp->next = prev;
  412.         prev = h;
  413.     }
  414.     Remount_list = prev;
  415.     /*
  416.      * Now mount the volumes
  417.      */
  418.     for (h = Remount_list; h ; h = next)
  419.     {
  420.         HLock (h);
  421.         mp = (mchain *)(*h);
  422.         next = mp->next;
  423.         error = mountsvr (&mp->info, false);
  424.         HUnlock (h);
  425.         if (error)
  426.             result = error;
  427.         DisposHandle (h);
  428.     }
  429.     Remount_list = 0;
  430.     return result;
  431. }
  432.  
  433.  
  434. /*
  435.  *=========================================================================
  436.  * unmount (vol) - unmount a volume
  437.  * entry:    vol = vRefNum to of volume to unmount
  438.  * returns:    OSErr
  439.  *=========================================================================
  440.  */
  441. OSErr
  442. unmount (vol)
  443.     Integer        vol;
  444. {
  445.     OSErr            error;
  446.     ParamBlockRec    pb;
  447.  
  448.     Clue0 = "\punmount";
  449.     if (vol == 0)
  450.         return 0;
  451.     if (vol == BootVol)
  452.         return 0;
  453.     ZERO(pb);
  454.     pb.volumeParam.ioVRefNum = vol;
  455.     error = PBUnmountVol (&pb);
  456.     if (error)
  457.     {
  458.         ClueID = error;
  459.         panic (true, E_SYS, Clue0, "\pPBUnmountVol", nil);
  460.     }
  461.     return error;
  462. }
  463.  
  464.  
  465. /*
  466.  *=========================================================================
  467.  * unmount_conflicts (volref, mi) - unmount volumes from given server
  468.  * entry:    volref = refnum of a mounted volume from server
  469.  *                    = 0 if none known
  470.  *            mi = ptr to mount info
  471.  *            Mounted_vref = vref of volume we have mounted
  472.  * exit:    volumes which might conflict with remounting of specified
  473.  *            volume have been unmounted
  474.  *            Information needed to remount them added to Remount_list.
  475.  *=========================================================================
  476.  */
  477. static
  478. void
  479. unmount_conflicts (Integer volref, AFPVolMountInfo *mi)
  480. {
  481.     AFPVolMountInfo    *ami;                /* info about mounted vol */
  482.     Integer            cinfosize;            /* current size of ami */
  483.     mchain            *cp;                /* chain pointer */
  484.     OSErr            error;
  485.     Handle            h;                    /* temp handle */
  486.     int                i;                    /* vol index */
  487.     Integer            infosize;            /* size needed to hold vol info */
  488.     char            *s;                    /* temp ptr into ami */
  489.     Longint            serverAdr;            /* network address of server */
  490.     Integer            t;                    /* temp */
  491.     Integer            vref;                /* vRefNum of current volume */
  492.     HParamBlockRec    pb;
  493.     GetVolParmsInfoBuffer    vib;
  494.     Str31            volName;            /* name of mounted vol */
  495.  
  496.     serverAdr = 0;
  497.     if (volref)
  498.     {
  499.         ZERO (pb);
  500.         ZERO (vib);
  501.         pb.ioParam.ioVRefNum = volref;
  502.         pb.ioParam.ioBuffer = (Ptr)&vib;
  503.         pb.ioParam.ioReqCount = sizeof(vib);
  504.         error = PBHGetVolParms(&pb, false);
  505.         if (error == paramErr)
  506.             return;                        /* system does not support needed calls */
  507.         if (error == 0)
  508.         {
  509.             serverAdr = vib.vMServerAdr;
  510.             if (serverAdr == 0)
  511.                 return;                    /* if not network volume, no conflicts */
  512.         }
  513.     }
  514.     /*
  515.      * Scan the mounted volumes looking for conflicting volumes
  516.      */
  517. rescan:
  518.     cinfosize = 0;
  519.     h = 0;
  520.     ZERO (pb);
  521.     ZERO (vib);
  522.     pb.volumeParam.ioNamePtr = volName;
  523.     error = 0;
  524.     for (i = 1; ; i++)
  525.     {
  526.         volName[0] = 0;
  527.         pb.volumeParam.ioVRefNum = 0;
  528.         pb.volumeParam.ioVolIndex = i;
  529.         error = PBHGetVInfo (&pb, false);
  530.         if (error)
  531.             break;                        /* error should mean end of vol list */
  532.         pb.ioParam.ioBuffer = (Ptr) &vib;
  533.         pb.ioParam.ioReqCount = sizeof(vib);
  534.         error = PBHGetVolParms(&pb, false);
  535.         if (error && error != paramErr)
  536.             break;                        /* give up on error */
  537.         /*
  538.          * If this volume is not networked, cannot conflict
  539.          */
  540.         if (error == paramErr || vib.vMServerAdr == 0)
  541.             continue;
  542.         /*
  543.          * If we know the server address and this vol is at a different address,
  544.          * it cannot conflict
  545.          */
  546.         if (serverAdr && serverAdr != vib.vMServerAdr)
  547.             continue;
  548.         /*
  549.          * We will probably unmount this volume, so get the remounting info
  550.          * for it
  551.          */
  552.         vref = pb.ioParam.ioVRefNum;
  553.         if (vref != Mounted_vref)
  554.         {
  555.             ZERO(pb);
  556.             infosize = 0;
  557.             pb.ioParam.ioVRefNum = vref;
  558.             pb.ioParam.ioBuffer = (Ptr)&infosize;
  559.             pb.ioParam.ioReqCount = sizeof(infosize);
  560.             error = PBGetVolMountInfoSize((ParmBlkPtr)&pb);
  561.             /*
  562.              * There are three possible outcomes from this call:
  563.              * It succeeds, which means we should be able to get
  564.              * the mounting info.
  565.              * It fails with error paramErr, which means we are
  566.              * running on an early system.  In this case,
  567.              * we unmount all network volumes because we cannot
  568.              * tell which are safe to leave mounted.
  569.              * It fails some other way, so we skip this volume.
  570.              */
  571.             if (error == paramErr || infosize < sizeof(*ami))
  572.                 infosize = sizeof (*ami);
  573.             if (infosize > cinfosize)    /* get a handle big enough for info */
  574.             {
  575.                 if (h)
  576.                     DisposHandle (h);
  577.                 h = NewHandle(infosize + sizeof(Handle));
  578.                 if (h == 0)
  579.                 {
  580.                     cinfosize = 0;
  581.                     continue;
  582.                 }
  583.                 cinfosize = infosize;
  584.             }
  585.             HLock(h);
  586.             cp = (mchain *)(*h);
  587.             ami = &cp->info;
  588.             if (error == 0)
  589.             {
  590.                 pb.ioParam.ioBuffer = (Ptr)ami;
  591.                 pb.ioParam.ioReqCount = cinfosize;
  592.                 error = PBGetVolMountInfo((ParmBlkPtr)&pb);
  593.             }
  594.             if (error && error != paramErr) {
  595.                 HUnlock(h);
  596.                 continue;
  597.             }
  598.             /*
  599.              * If GetVolMountInfo not supported, fake answer by
  600.              * assuming all network volumes are from same server
  601.              * and under guest account
  602.              */
  603.             if (error == paramErr)
  604.             {
  605.                 s = ami->AFPData;
  606.                 ZEROAT(ami);
  607.                 ami->length = infosize;
  608.                 ami->media = mi->media;
  609.                 ami->uamType = kNoUserAuthentication;
  610.                 if (t = mi->zoneNameOffset)
  611.                     COPYPS((char *)mi + t, s);
  612.                 ami->zoneNameOffset =        s - (char *)ami;    s += 32;
  613.                 if (t = mi->serverNameOffset)
  614.                     COPYPS((char *)mi + t, s);
  615.                 ami->serverNameOffset =        s - (char *)ami;    s += 32;
  616.                 COPYPS(volName, s);
  617.                 ami->volNameOffset =        s - (char *)ami;    s += 32;
  618.                 ami->userNameOffset =        s - (char *)ami;    s += 32;
  619.                 ami->userPasswordOffset =    s - (char *)ami;    s += 32;
  620.                 ami->volPasswordOffset =    s - (char *)ami;
  621.             } else {
  622.                 /*
  623.                  * If different servers, skip volume
  624.                  */
  625.                 if (!EqualString ((unsigned char *)mi + mi->serverNameOffset,
  626.                             (unsigned char *)ami + ami->serverNameOffset,
  627.                             false, true))
  628.                 {
  629.                     HUnlock(h);
  630.                     continue;
  631.                 }
  632.             }
  633.             HUnlock(h);
  634.         }
  635.         /*
  636.          * Need to unmount this volume
  637.          */
  638.         error = unmount (vref);
  639.         if (error)
  640.         {
  641.             if (Quit)
  642.                 return;
  643.             continue;
  644.         }
  645.         /*
  646.          * If not vol mounted by selves, add to list of unmounted vols
  647.          */
  648.         if (vref == Mounted_vref)
  649.         {
  650.             Mounted_vref = 0;
  651.         }
  652.         else
  653.         {
  654.             cp = (mchain *)(*h);
  655.             cp->next = Remount_list;
  656.             Remount_list = h;
  657.         }
  658.         /*
  659.          * Now, have to rescan from top, since vols get renumbered when
  660.          * one is unmounted.
  661.          */
  662.         goto rescan;
  663.     }
  664.     if (h)
  665.         DisposHandle(h);
  666. }
  667.  
  668.  
  669.  
  670.  
  671. /*
  672.  *=========================================================================
  673.  * unmount_mounted (vol) - unmount volumes we mounted
  674.  * entry:    vol = vRefNum to unmount in addition to Mounted_vref
  675.  * returns:    OSErr
  676.  *=========================================================================
  677.  */
  678. OSErr
  679. unmount_mounted (Integer vol)
  680. {
  681.     OSErr        error;
  682.  
  683.     error = 0;
  684.     if (vol && vol != Mounted_vref)
  685.         error = unmount (vol);
  686.     if (Mounted_vref)
  687.         error = unmount (Mounted_vref);
  688.     Mounted_vref = 0;
  689.     return error;
  690. }